#!/usr/bin/python2.5
# Copyright 2009 Joe LaPenna
"""Add a new crash for a product."""

import datetime
import logging
import urllib
import urlparse
import hashlib
import hmac

from google.appengine.api import memcache
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp

import model


class Add(webapp.RequestHandler):
  """Add a crash for a given product."""

  def post(self):
    # Verify Authenticity of request
    if (self.message_is_valid()):
      logging.debug('message verified.')
    else:
      logging.debug('message verification failed.')
      self.error(401)
      return

    # Put request in datastore
    product = model.Product.find(self.request.get('product_key', None))
    client = self.request.get('client', None)
    short = self.request.get('short', None)
    long = self.request.get('long', None)
    tag = self.request.get('tag', None)

    severity = self.request.get('severity', None)
    if severity:
      severity = int(severity)

    # If the user has provided a date, we have to convert it to a datetime.
    # We assume the user sent us the request in UTC+0
    date = self.request.get('date', None)
    if date:
      # TODO(jlapenna): Move this constant somewhere sane.
      date =  datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S')
    else:
      date =  datetime.datetime.utcnow()

    crash = model.Crash(product=product, client=client, date=date, short=short,
                        long=long, tag=tag, severity=severity)
    crash.put()

  def message_is_valid(self):
    logging.debug('arguments' + str(self.request.arguments()))
    product_key = self.request.get('product_key')
    product = model.Product.find(product_key)
    if not product:
      logging.debug('Message Invalid: Missing or invalid product_key')
      return False

    request_digest = self.request.get('hmac')
    if not request_digest:
      logging.debug('Message Invalid: Missing hmac')
      return False

    # Build the message we're going to sign.
    all_parameters = [(key, self.request.get(key))
                      for key in self.request.arguments()]
    logging.debug('all_parameters: ' + str(all_parameters))

    # Remove things that wouldn't have been in the original hash
    parameters_for_message = [pair for pair in all_parameters
                              if pair[0] != 'hmac']
    logging.debug('parameters_for_message: ' + str(parameters_for_message))

    # Sort them by key name.
    parameters_for_message.sort(key=lambda k: k[0])
    message = '&'.join(['%s=%s' % pair for pair in parameters_for_message])
    logging.debug('message: ' + message)

    # Find out what we would expect this request's hmac digest to look like.
    expected_secret = product.secret
    logging.debug('expected_secret: ' + expected_secret)
    expected_hmac = hmac.HMAC(expected_secret, message, digestmod=hashlib.sha1)
    expected_digest = expected_hmac.hexdigest()

    # compare the expected_digest to what was provided in the request
    valid = expected_digest == request_digest
    logging.debug('message_is_valid: ' + str(valid))
    if not valid:
      logging.debug(
        'Expecting: %s, found: %s ' % (expected_digest, request_digest))

    return valid


def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
  """Parse a query given as a string argument.

  Shamelessly cribbed from python2.6
    -jla

  Arguments:

  qs: URL-encoded query string to be parsed

  keep_blank_values: flag indicating whether blank values in
      URL encoded queries should be treated as blank strings.  A
      true value indicates that blanks should be retained as blank
      strings.  The default false value indicates that blank values
      are to be ignored and treated as if they were  not included.

  strict_parsing: flag indicating what to do with parsing errors. If
      false (the default), errors are silently ignored. If true,
      errors raise a ValueError exception.

  Returns a list, as G-d intended.
  """
  pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
  r = []
  for name_value in pairs:
    if not name_value and not strict_parsing:
      continue
    nv = name_value.split('=', 1)
    if len(nv) != 2:
      if strict_parsing:
        raise ValueError, "bad query field: %r" % (name_value,)
      # Handle case of a control-name with no equal sign
      if keep_blank_values:
        nv.append('')
      else:
        continue
    if len(nv[1]) or keep_blank_values:
      name = unquote(nv[0].replace('+', ' '))
      value = unquote(nv[1].replace('+', ' '))
      r.append((name, value))

  return r
